/*
**	This game has been created with the player in mind. There is no net punishment due to security, which means this is a very insecure game.
**	There will be eventual crosschecks to see if the commands players are sending are valid, but right now the security is very relaxed.
**	To guarantee the best gameplay with the gamemode, an anti-cheat is suggested.
*/

GM.Name = "Initial G"
GM.Author = ""
GM.Email = ""
GM.Website = ""
GM.TeamBased = false

include( "player_meta.lua" )
include( "entity_meta.lua" )
include( "sh_hooks.lua" )
include( "synched_vars.lua" )
include( "weapons.lua" )
include( "file_reload.lua" )
include( "blocks.lua" )

DeriveGamemode( "base" )

/*
Possible Types:
SYNCVAR_SHORT
SYNCVAR_LONG
SYNCVAR_FLOAT
SYNCVAR_STRING
SYNCVAR_VECTOR
SYNCVAR_ENTITY
SYNCVAR_BOOL
*/

require( "syncvar" )

syncvar.Register( "num_checkpoints", SYNCVAR_SHORT )
syncvar.Register( "num_laps", SYNCVAR_SHORT )
syncvar.Register( "waiting", SYNCVAR_BOOL )

syncvar.RegisterEntity( "lap_number", SYNCVAR_SHORT, false ) -- The third argument signifies that this var is only sent to the player it is set on.
syncvar.RegisterEntity( "lap_start_time", SYNCVAR_FLOAT, false )
syncvar.RegisterEntity( "checkpoint_number", SYNCVAR_SHORT, false )
syncvar.RegisterEntity( "current_place", SYNCVAR_SHORT, false )
syncvar.RegisterEntity( "cash", SYNCVAR_SHORT, false )
syncvar.RegisterEntity( "best_time", SYNCVAR_FLOAT )
syncvar.RegisterEntity( "ready", SYNCVAR_BOOL )
syncvar.RegisterEntity( "weapon", SYNCVAR_STRING )
syncvar.RegisterEntity( "optimism", SYNCVAR_STRING, true )
syncvar.RegisterEntity( "mana", SYNCVAR_SHORT, true )
syncvar.RegisterEntity( "health", SYNCVAR_SHORT )
syncvar.RegisterEntity( "aim_vector", SYNCVAR_VECTOR )
syncvar.RegisterEntity( "move_velocity", SYNCVAR_VECTOR )

syncvar.RegisterEntity( "blinking", SYNCVAR_BOOL )

util.PrecacheModel( "models/manhack.mdl" )

/*
**	Unsafe prediction variables.
*/
local lastAngle = Angle(0,0,0)
local lastForwardMovement = 0
local stopDrift = 0
local lastDir = 0
local turnVel = 0
local lastVel = Vector(0,0,0)

function GM:Buggify( pl )

	pl:SetModel( "models/player/kleiner.mdl" )
	pl:SetCanWalk()
	pl:SetDuckSpeed( 0.000001 )
	pl:SetUnDuckSpeed( 0.000001 )
	pl:SetViewOffset( Vector( 0,0,50 ) )
	pl:SetViewOffsetDucked( Vector( 0,0,50 ) )
	pl:ResetHull()
	pl:SetWalkSpeed( 10 )
	pl:SetHull( Vector(-5,-5,0), Vector( 5,5,10 ) )
	pl.ViewAngle = Angle()
	pl.MoveDir = 100
	pl.BuggyAngle = Angle()
	pl:SetColor( 255,255,255,255 )
	pl.NextAttack = 0
	pl:SetMoveVelocity( Vector( 10,10,0 ) )
	pl:SetWalkSpeed( 1 )
	pl:SetStepSize( 1 )
	
	if CLIENT and tostring( pl.Buggy ) == "nil" then
		pl.Buggy = pl.Buggy or ClientsideModel( "models/buggy.mdl" )
		pl.Buggy:SetNoDraw( true )
		pl.Creator = pl.Creator or ClientsideModel( "models/props_phx/construct/concrete_barrier00.mdl" )
		pl.WorldModel = pl.WorldModel or ClientsideModel( "models/props_phx/construct/concrete_barrier00.mdl" )
		RunConsoleCommand( "request_sync" )
	end
	
	lastAngle = Angle(0,180,0)
	lastForwardMovement = 0
	stopDrift = 0
	lastDir = 0
	turnVel = 0
	lastVel = Vector(0,0,0)
	
	if SERVER then
		pl:SetCheckpoint( 0 )
	end
	
	MsgN( "asdawdf" )
	
end

if CLIENT then

function math.sign( x )
	if x > 0 then
		return 1
	elseif x < 0 then
		return -1
	end
	return 0
end

function GM:CreateMove( ucmd )

	if not syncvar.GetEntity( LocalPlayer(), "optimism" ) then return end

	LocalPlayer().MoveDir = LocalPlayer().MoveDir or 100
	LocalPlayer().ViewAngle = LocalPlayer().ViewAngle or Angle()
	LocalPlayer().ViewAngle = LocalPlayer().ViewAngle + ( lastAngle - Angle( 0,LocalPlayer().MoveDir,0 ) )

	if SinglePlayer() then
		GAMEMODE:GenerateMovement( LocalPlayer() )
	end
	
	if ucmd:GetButtons() & IN_JUMP > 0 then
		ucmd:SetButtons( ucmd:GetButtons() - IN_JUMP )
	end
	
	if ucmd:GetButtons() & IN_DUCK > 0 then
		ucmd:SetButtons( ucmd:GetButtons() - IN_DUCK )
	end
	
	LocalPlayer().lastDir = LocalPlayer().lastDir or 0
	
	local localVel = LocalPlayer():GetLocalMoveVelocity()
	local fVel = LocalPlayer():GetMoveVelocity():Length()
	if fVel > 0 then
		ucmd:SetForwardMove( 10000 )
	else
		ucmd:SetForwardMove( 0 )
	end
	
	if ucmd:GetButtons() & IN_MOVELEFT > 0 and fVel > 5 then
		turnVel = math.Approach( turnVel, 100, FrameTime()*100 )
	elseif ucmd:GetButtons() & IN_MOVERIGHT > 0 and fVel > 5 then
		turnVel = math.Approach( turnVel, -100, FrameTime()*100 )
	else
		turnVel = math.Approach( turnVel, 0, FrameTime()*100 )
	end
	
	LocalPlayer().MoveDir = LocalPlayer().MoveDir + FrameTime()*turnVel
	
	LocalPlayer().MoveDir = math.fmod(LocalPlayer().MoveDir,360)
	
	ucmd:SetViewAngles( LocalPlayer():GetMoveVelocity():Angle() )
	ucmd:SetSideMove( 0 )
	ucmd:SetUpMove( 0 )
	lastAngle = Angle( 0,LocalPlayer().MoveDir,0 )
	RunConsoleCommand( "real_angle", LocalPlayer().MoveDir, syncvar.Get( "optimism" ) )
	lastVel = lastAngle:Forward()*LocalPlayer():GetWalkSpeed()
end

function GM:GetFakeEyePos()
	return lastAngle:Forward()*-25 + EyePos() - Vector(0,0,35)
end

function GM:GetFakeAngles()
	return lastAngle
end
	
function GM:CalcView( pl, pos, angles, fov )
	local lastAngle = Angle( 0,pl.MoveDir,0 )
	pl.SmoothAngle = LerpAngle( 0.1, pl.SmoothAngle or Angle(0,0,0), lastAngle )
	local trRes = util.TraceLine( { start=pl:GetPos(), endpos=pl.SmoothAngle:Forward()*-25 + pos - Vector(0,0,35), filter={ pl, pl.Buggy } } )

	local view = {
		origin = trRes.HitPos,
		angles = pl.SmoothAngle,
		fov = fov
	}
	return view
end

end

local lastMovement = CurTime()
function GM:GenerateMovement( pl )
	local frametime = CurTime() - lastMovement
	lastMovement = CurTime()
	
	pl.MoveDir = pl.MoveDir or 100
	local lastAngle = Angle( 0,pl.MoveDir,0 )
	
	if pl:KeyDown( IN_FORWARD ) then
	
			pl:SetMoveVelocity( pl:GetMoveVelocity() + lastAngle:Forward() * (500* frametime) )
	
	elseif pl:KeyDown( IN_BACK ) then
	
			pl:SetMoveVelocity( pl:GetMoveVelocity() - lastAngle:Forward() * (500* frametime) )
		
	else
		
		-- If we're not movement, add friction.
		local to_sub = pl:GetMoveVelocity() * 2 * frametime
		if ( to_sub:Length() > pl:GetMoveVelocity():Length() ) then
			pl:SetMoveVelocity( pl:GetMoveVelocity() - pl:GetMoveVelocity() * 10 * frametime )
		else
			pl:SetMoveVelocity( pl:GetMoveVelocity() - pl:GetMoveVelocity() * 10 * frametime )
		end
		
	end
	
	-- Clamp velocity
	if ( pl:GetMoveVelocity():Length() > 185/*Max movement speed*/) then
		pl:SetMoveVelocity( pl:GetMoveVelocity():GetNormal() * 185 )
	end
	
	if SERVER then
		pl:SetWalkSpeed( pl:GetMoveVelocity():Length() )
	else
		-- Henry; it was way too laggy for the client to predict the movement speed.
		-- Henry; fixed lag.
		pl:SetWalkSpeed( pl:GetMoveVelocity():Length() )
	end
end

function GM:Move( pl, mv )
	GAMEMODE:GenerateMovement( pl )
end
